<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>拼图</title>
    <style>
        body {
  display: flex;
  justify-content: center;
  align-items: center;
  min-height: 100vh;
}

#captcha {
  --width: 400px;
  --height: 260px;
  --puzzle-width: 80px;
  --puzzle-height: 80px;
  --offset-left: var(--width) * -1;
  --moved: 0px;
  
  display: block;
  width: var(--width);
  height: var(--height);
  border-radius: 4px;
  background-image: url(https://tse1-mm.cn.bing.net/th/id/R-C.c6c03edea530e9caa677c9d17f193a4d?rik=MBgpsjumbTD5eQ&riu=http%3a%2f%2fwww.desktx.com%2fd%2ffile%2fwallpaper%2fscenery%2f20170209%2fca186d97701674b996264b2d352894a7.jpg&ehk=HunG%2fPF7pUbpcS34cWpNvlS%2faoDPbcaTYL6LFFPQIIM%3d&risl=&pid=ImgRaw&r=0);
  background-size: cover;
  background-position: center;
  position: relative;
  box-shadow: 0px 2px 4px rgba(0, 0, 0, .3);
}

#captcha::before, #captcha::after {
  position: absolute;
  content: '';
  display: block;
  width: inherit;
  height: inherit;
  background-image: inherit;
  background-size: inherit;
  background-position: inherit;
  
  clip-path: inset(
    calc( (var(--height) - var(--puzzle-height)) / 2 ) /* 90px */
    var(--puzzle-width) /* 90px */
    calc( (var(--height) - var(--puzzle-height)) / 2 ) /* 90px */
    calc( var(--width) - var(--puzzle-width) * 2 ) /* 230px */
  );
  -webkit-clip-path: inset(
    calc( (var(--height) - var(--puzzle-height)) / 2 ) /* 90px */
    var(--puzzle-width) /* 90px */
    calc( (var(--height) - var(--puzzle-height)) / 2 ) /* 90px */
    calc( var(--width) - var(--puzzle-width) * 2 ) /* 230px */
  );
}

#captcha::after {
  transform: translatex(
    clamp(
      calc( var(--width) * -1 ),
      calc( (var(--width) * -1) + var(--moved) ),
      calc( var(--puzzle-width) )
    )
  );
  transition: .25s all ease-in-out;
}

#captcha:active::after {
  transition: none;
}

#captcha::before {
  background-color: rgba(0, 0, 0, .6);
  background-blend-mode: multiply;
}

#handle {
  width: calc( var(--width) + var(--puzzle-width) * 2 );
  height: 30px;
  border-radius: 18px;
  background-color: #eee;
  position: absolute;
  bottom: -50px;
  left: calc( var(--puzzle-width) * 2 * -1 );
  box-shadow: inset 0px 0px 12px rgba(0, 0, 0, .2);
  border: 3px solid #ccc;
}

#handle span {
  display: block;
  width: var(--puzzle-width);
  height: inherit;
  border-radius: inherit;
  background-color: #fff;
  box-shadow: inset 0px 0px 6px rgba(0, 0, 0, .25), 0px 2px 4px rgba(0, 0, 0, .3);
  position: absolute;
  cursor: move;
  transform: translatex(
    clamp(
      0px,
      var(--moved),
      calc( var(--width) + var(--puzzle-width) )
    )
  );
  
  transition: .25s all ease-in-out;
}

#captcha:active #handle span {
  transition: none;
}

#captcha.passed::before,
#captcha.passed::after,
#captcha.passed #handle {
  opacity: 0;
}
    </style>
</head>
<body>
    <div id="captcha">
        <div id="handle">
          <span></span>
        </div>
      </div>
</body>
<script>
    let shouldMove = false
const captcha = document.querySelector('#captcha')
const handle = document.querySelector('#handle')
const button = document.querySelector('#handle span')

button.addEventListener('mousedown', (e) => {
  shouldMove = true
})

window.addEventListener('mousemove', (e) => {
  if (shouldMove) {
    const offsetLeft = handle.getBoundingClientRect().left
    const buttonWidth = button.getBoundingClientRect().width
    
    captcha.style.setProperty('--moved', `${e.clientX - offsetLeft - buttonWidth / 2}px`)
  }
})

window.addEventListener('mouseup', (e) => {
  if (shouldMove) {
    const finalOffset = e.clientX - handle.getBoundingClientRect().left
    
    if (finalOffset >= 430 && finalOffset <= 450) {
      // pass
      captcha.classList.add('passed')
      alert("444")
    } else {
      // failed
      captcha.style.setProperty('--moved', '0px')
    }
    
    shouldMove = false
  }
})
</script>
</html>